//泛型：只要发生于这三类类型中函数、类、接口,
//主要是解决于在设计之处的时候不确定真实使用的值是什么类型，而在真正使用的时候才来确定数据类型.
//泛型的使用只要靠泛型变量来真正带入使用的值的具体类型
//泛型变量特点：大写，一个字母
//常见的泛型变量：T、P、I、R、U...
//1、泛型函数只要发生与在函数的形式参数上在设计之处数据类型不确定的时候，需要变量，在真正调用函数的时候给出什么数据类型则这个泛型变量就表示什么数据类型

//案例：要求批量创建数组，值的类型不限制，数量也不限制
//['1','2','3']
//[1,2,3]
//[true,false]

//定义函数
// function createArray<T>(count: number, value: T) {
//     //创建一个数组
//     let arr: T[] = [];
//     //循环写入
//     for (let i = 0; i < count; i++) {
//         arr.push(value);
//     }
//     //返回
//     return arr;
// }
// //调用函数
// let result = createArray<string>(5, 'hello ts');//['hello ts', 'hello ts', 'hello ts', 'hello ts', 'hello ts']
// console.log(result);
// //打印数组中的某一个值并且获取其长度
// //情况1：发现没有提示
// //情况2：如果硬写，写对了也可以执行，就怕写不对
// console.log(result[1].length)

// let result1 = createArray<number>(3, 34.456); //[34.456, 34.456, 34.456]
// console.log(result1);
// console.log(result1[0].toFixed(2));

//在定义函数的形式参数上有多个形式参数数据类型都不确定，则可以定义多个泛型变量
// function fun<T, P, R>(a: number, b: string, c: T, d: P, e: R): [T, P, R] {
//     console.log(a);
//     console.log(b);
//     console.log(c);
//     console.log(d);
//     return [c, d, e];
// }

// let f1 = fun(1, 'hello', true, { a: 1, b: 2 }, ['尚硅谷', '百度', '小米']);
// console.log(f1);
// console.log(f1[1].a);
// console.log(f1[1].b);

//2、泛型接口
//接口：对象的结构类型限制
//泛型：不确定
//泛型接口：主要解决与不确定对象结构的时候（有可能是一种对象结构，也有可能是另外一种对象结构）
//语法：let 变量:接口名称<泛型变量>:{}

//以ajax响应的结果为例
//{config、data、headers、request、status、statusText}

//以todolist响应结果定义的接口
// interface Todolist {
//     id: number;
//     title: string;
//     done: boolean
// }
// //以图书管理系统响应结果定义的接口
// interface BookLists {
//     id: number;
//     name: string;
//     price: number;
//     autor: string;
// }

// //整体响应结果
// interface Res<T> {
//     Status: number;
//     StatusText: string;
//     data: T
// }

// let obj: Res<Todolist> = {
//     Status: 200,
//     StatusText: 'ok',
//     data: {
//         id: 1,
//         title: '任务1',
//         done: true
//     }
// }
// console.log(obj);

// let obj1: Res<BookLists> = {
//     Status: 200,
//     StatusText: 'ok',
//     data: {
//         id: 1001,
//         name: '茶花女',
//         price: 34.67,
//         autor: '张三'
//     }
// }
// console.log(obj1);

//3、泛型类
//类：批量产生实例化对象，重在于对象属性值有可能不确定
//泛型：不确定

// interface Todolist {
//     id: number;
//     title: string;
//     done: boolean
// }

// class Student<T> {
//     name: string;
//     age: number;
//     sex: string;
//     hobby: T[];

//     constructor(n: string, a: number, s: string, h: T[]) {
//         this.name = n;
//         this.age = a;
//         this.sex = s;
//         this.hobby = h;
//     }
// }

// let s1 = new Student<string>('张三', 43, '男', ['抽烟', '打牌', '喝酒', '烫个头']);
// console.log(s1);
// console.log(s1.hobby[0].length);

// let s2 = new Student<number>('李四', 44, '男', [1, 2, 3, 4]);
// console.log(s2.hobby[0].toString());

// //注意：只要的使用泛型变量将来传递的数据为数组中的对象的话，则类型通常会使用接口来规范(泛型约束)
// let s3 = new Student<Todolist>('李四', 44, '男', [{ id: 1, title: '任务1', done: true }]);
// console.log(s3.hobby[0].id);

// //数组
// function f1(a: Array<string>) {
//     console.log(a);
// }
// f1(['a', 'b', 'c']);


//其他
//1.1 类型声明

// let s: string = 'hello';
// //语法：type 类型名称 = 值;
// //typeof 可以检测number、string、boolean、函数、null等类型
// //intanceof 可以检测array、object
// //类型只能声明，不能输出
// type str = typeof s;

// let s1: str;
// s1 = 'hello ts';
// console.log(s1);

// let n: number = 100;

// type num = typeof n;

// let n1: num = 200;
// console.log(n1);

// interface booklist {
//     id: number;
//     bookname: string;
//     price: number
// }

// type arrs = booklist[];

// type arr2 = string[];

// let arr1: arr2 = ['a', 'b', 'c']

// let arr: arrs = [{ id: 1, bookname: '茶花女', price: 34 }, { id: 2, bookname: '寓言故事', price: 56 }];

// //如果获取函数的返回值作为一个类型的声明
// function fun() {
//     return [1, 3, 4]
// }

// type s1 = ReturnType<typeof fun>; //number[]

// let num: s1 = [10, 20, 30];
// console.log(num);

// //如果获取函数的形参数据类型作为一个类型的声明
// function fun1(a: number, b: string) {

// }
// // //Parameters泛型返回的是一个元祖类型
// type f11 = Parameters<typeof fun1>; // [number,string]

// let a: f11 = [1, 'a'];
// console.log(a);

// //typeof:可以检测number、string、boolean、函数、null等类型
// //keyof:用来获取接口、类等的所有属性名组成的联合类型(属性名1 | 属性名2....)。

// interface F1 {
//     a: number;
//     b: string
// }

// type f = keyof F1;  // a | b

// function fun2(x: f) {
//     console.log(x);
// }
// fun2('a');


// class Student {
//     name: string;
//     age: number;
//     sex: string;
//     constructor(n: string, a: number, s: string) {
//         this.name = n;
//         this.age = a;
//         this.sex = s;
//     }
// }

// type f1 = keyof Student; //name | age | sex

// function fun13(x: f1) {
//     console.log(x);
// }
// fun13('name');
// fun13('age');
// fun13('sex');


// 练习题1: 为当前对象声明一个接口并使用
// 例如：{ id: 1, name: "茶花女", price: 23.56, img: 1.jpg, time: '2023-03-02 13:23:34', autor: '张三' }

// interface fun {
//     id: number,
//     name: string,
//     price: number,
//     img: string,
//     time: string,
//     autor: string
// }
// let fun1: fun = {
//     id: 1,
//     name: "茶花女",
//     price: 23.56,
//     img: '1.jpg',
//     time: '2023-03-02 13:23:34',
//     autor: '张三',
// }
// console.log(fun1);

// 练习题2: 声明一个继承类，其中包含所有的成员修饰符(public、protected、private)
// class Father {
//     public id: number;
//     protected name: string;
//     private price: number;

//     constructor(id: number, name: string, price: number) {
//         this.name = name;
//         this.id = id;
//         this.price = price;
//     }
// }
// class Son extends Father {
//     title: string;
//     constructor(id: number, name: string, price: number, title: string) {
//         super(id, name, price);
//         this.title = title;
//     }
// }
// let s1 = new Son(1001, '茶花女', 45, '任务1');
// console.log(s1);

// 练习题3: 声明一个泛型类，其中包含姓名，性别，年龄，地址
// class Student<T>{
//     name: string;
//     sex: string;
//     age: number;
//     add: T;
//     constructor(name: string, sex: string, age: number, add: T) {
//         this.name = name;
//         this.sex = sex;
//         this.age = age;
//         this.add = add;
//     }
// }
// let s2 = new Student("张三", "男", 20, "北京")
// console.log(s2.add.length);

// let s3 = new Student("张三", "男", 20, ["北京", "上海", '重庆'])
// console.log(s3.add.join('/'));


// 练习题4: 声明一个数组类型(type)，数组中包含对象，属性名分别为a和b，数据类型为number
// interface F1 {
//     a: number;
//     b: number;
// }
// type arr = F1[];

// let aa: arr = [{ a: 1, b: 2 }];
// console.log(aa);


//1.2 内置类型

// let a: Array<string> = new Array('a', 'b');
// let n: Number = new Number(10);
// console.log(n);

// let b: Boolean = new Boolean(true);
// console.log(b);

// // new String();
// // new RegExp(/hello/i);
// // new Error();

// let now: Date = new Date();
// console.log(now);

//通过dom获取div元素
//所以只有获取单一一个元素的时候需要下断言
let box1 = document.getElementById('box') as HTMLDivElement;
console.log(box1);

let p = document.getElementsByTagName('p')
console.log(p); // []

let spans = document.querySelectorAll('span');
console.log(spans); // []

